#include <gtest/gtest.h>
#include <memory>
#include <map>
#include "lua/lua.hpp"
using namespace std;
lua_State *l, *mainl, *subl;
int mainCoIndex = -1;
class JksMutithread : public testing::Test
{
public:
    virtual void SetUp()
    {
        l = luaL_newstate();
        luaL_openlibs(l);
        mainl = lua_newthread(l);
        subl = lua_newthread(l);
    }
    virtual void Teardown() { lua_close(l); }
};

static void hook(lua_State* L, lua_Debug* ar)
{
    lua_getinfo(L, "tnlSur", ar);
    printf("%d\n", ar->event);
    return;
}

TEST_F(JksMutithread, hooktest)
{
    string cmd = "print(\"t1\")\nprint(\"t2\")\nprint(\"t3\")";
    lua_sethook(l, hook, LUA_MASKLINE | LUA_MASKCALL | LUA_MASKRET, 0);
    int ret = luaL_dostring(l, cmd.c_str());
}

TEST_F(JksMutithread, mtsim)
{
    int ret = luaL_dofile(l, "src/scheduler/test/mtsim.jks");
    ASSERT_EQ(ret, LUA_OK);
}
static int __auxresume(lua_State* L, lua_State* co, int narg)
{
    int status;
    if (!lua_checkstack(co, narg))
    {
        lua_pushliteral(L, "too many arguments to resume");
        return -1; /* error flag */
    }
    if (lua_status(co) == LUA_OK && lua_gettop(co) == 0)
    {
        lua_pushliteral(L, "cannot resume dead coroutine");
        return -1; /* error flag */
    }
    lua_xmove(L, co, narg);
    status = lua_resume(co, L, narg);
    if (status == LUA_OK || status == LUA_YIELD)
    {
        int nres = lua_gettop(co);
        if (!lua_checkstack(L, nres + 1))
        {
            lua_pop(co, nres); /* remove results anyway */
            lua_pushliteral(L, "too many results to resume");
            return -1; /* error flag */
        }
        lua_xmove(co, L, nres); /* move yielded values */
        return nres;
    }
    else
    {
        lua_xmove(co, L, 1); /* move error message */
        return -1;           /* error flag */
    }
}
class _TaskTest
{
public:
    _TaskTest(int ref) : _luaStateRef(ref) { runmode = 1; }
    ~_TaskTest() { luaL_unref(l, LUA_REGISTRYINDEX, _luaStateRef); }

    int _luaStateRef;
    int runmode;  //0:stop 1:run 2:pause
};
map<string, shared_ptr<_TaskTest>> taskContainer;

static int create_thread(lua_State* L)
{
    if (!lua_isstring(L, 1))
        return 0;
    string path = lua_tostring(L, 1);

    if (!lua_isstring(L, 2))
        return 0;
    string name = lua_tostring(L, 2);

    //前置处理

    //无阻塞状态机

    // 参考loadfile + cocreate
    int status = luaL_loadfilex(L, path.c_str(), NULL);  //此时L的栈自顶向下：function
    lua_State* NL = lua_newthread(L);                    //此时L的栈自顶向下：thread,function
    lua_pushvalue(L, -2);                                //此时L的栈自顶向下：function,thread,function
    lua_xmove(L, NL, 1);                                 //此时L的栈自顶向下：thread,function 此时NL的栈自顶向下：function
    lua_xmove(L, l, 1);                                  //将thread移动到调度器的lua_State中，用以后续调度
    mainCoIndex = luaL_ref(l, LUA_REGISTRYINDEX);        //引用调度器中thread
    auto t = make_shared<_TaskTest>(mainCoIndex);
    taskContainer.insert(pair<string, shared_ptr<_TaskTest>>(name, t));
    //后置处理
    return 0;
}
static int thread_join(lua_State* L) { return 0; }
static int isCoDead(lua_State* L)
{
    if (lua_status(L) == LUA_OK)
    {
        //参考luaB_costatus dead状态判定
        lua_Debug ar;
        auto stack = lua_getstack(L, 0, &ar);
        auto top = lua_gettop(L);
        if (stack <= 0 && top == 0)
        {
            return 1;
        }
    }
    return 0;
}
TEST_F(JksMutithread, mutitasktest)
{
    lua_register(l, "create_thread", create_thread);
    luaL_loadfilex(l, "src/scheduler/test/mt2.jks", NULL);
    lua_State* mainthread = lua_newthread(l);
    lua_pushvalue(l, -2);
    lua_xmove(l, mainthread, 1);
    while (1)
    {
        int rrr = __auxresume(l, mainthread, 0);  // 启动主线程
        //调度子线程
        for (auto t : taskContainer)
        {
            if (t.second->runmode == 1)
            {
                lua_rawgeti(l, LUA_REGISTRYINDEX, t.second->_luaStateRef);
                lua_State* co = lua_tothread(l, -1);
                __auxresume(l, co, 0);
            }
        }
        if (isCoDead(mainthread))
        {
            int subThreadDeadFlag = 1;
            for (auto t : taskContainer)
            {
                lua_rawgeti(l, LUA_REGISTRYINDEX, t.second->_luaStateRef);
                lua_State* co = lua_tothread(l, -1);
                subThreadDeadFlag &= isCoDead(co);
            }
            if (subThreadDeadFlag)
            {
                break;
            }
        }
    }
}